/*
 * Copyright (C) 2008 Universidade Federal de Campina Grande
 *  
 * This file is part of OurGrid. 
 *
 * OurGrid is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free 
 * Software Foundation, either version 3 of the License, or (at your option) 
 * any later version. 
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public License 
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package org.ourgrid.peer.controller;

import org.ourgrid.common.interfaces.AccountingAggregator;
import org.ourgrid.common.interfaces.WorkerSpecListener;
import org.ourgrid.common.interfaces.management.WorkerManagement;
import org.ourgrid.common.interfaces.management.WorkerManagementClient;
import org.ourgrid.common.interfaces.to.LocalWorkerState;
import org.ourgrid.common.statistics.control.WorkerControl;
import org.ourgrid.peer.PeerConstants;
import org.ourgrid.peer.controller.messages.WorkerMessages;
import org.ourgrid.peer.dao.AllocationDAO;
import org.ourgrid.peer.dao.LocalWorkersDAO;
import org.ourgrid.peer.to.LocalWorker;
import org.ourgrid.reqtrace.Req;

import br.edu.ufcg.lsd.commune.api.FailureNotification;
import br.edu.ufcg.lsd.commune.api.InvokeOnDeploy;
import br.edu.ufcg.lsd.commune.api.RecoveryNotification;
import br.edu.ufcg.lsd.commune.container.servicemanager.ServiceManager;
import br.edu.ufcg.lsd.commune.identification.DeploymentID;
import br.edu.ufcg.lsd.commune.identification.ServiceID;

/**
 * Implement Peer actions when a local worker recover or fail.
 */
public class WorkerNotificationController {

	private ServiceManager serviceManager;

	@InvokeOnDeploy
	public void init(ServiceManager serviceManager) {
		this.serviceManager = serviceManager;
	}
	
	/**
	 * Notifies that a Worker has failed
	 */
	@Req("REQ019")
	@FailureNotification
	public void doNotifyFailure(WorkerManagement failedWorker, DeploymentID failedWorkerID) {
		
		LocalWorkersDAO workerDAO = serviceManager.getDAO(LocalWorkersDAO.class);
		LocalWorker localWorker = WorkerControl.getInstance().getLocalWorker(failedWorkerID.getServiceID(), workerDAO);
		
		if(localWorker == null){
			serviceManager.getLog().error(WorkerMessages.getNonExistentWorkerFailureMessage(failedWorkerID));
			return;
		} 
		
		if(!localWorker.isUp()){
			serviceManager.getLog().error(WorkerMessages.getNonRecoveredWorkerFailureMessage(failedWorkerID));
			return;
		} 
		
		
//		LocalAllocableWorker localAllocableWorker = 
//			serviceManager.getDAO(AllocationDAO.class).getLocalAllocableWorker(failedWorkerID.getPublicKey());
//		if (localAllocableWorker != null && localAllocableWorker.isDelivered()) {
//			return; //TODO log
//		}
		
		workerFailure(serviceManager, failedWorkerID.getServiceID(), localWorker);
	}

	public static void workerFailure(ServiceManager serviceManager, ServiceID failedWorkerID, 
			LocalWorker localWorker) {
		
		LocalWorkersDAO workerDAO = serviceManager.getDAO(LocalWorkersDAO.class);
		serviceManager.getLog().info(WorkerMessages.getFailedWorkerMessage(failedWorkerID));
		
		WorkerControl.getInstance().statusChanged(failedWorkerID, LocalWorkerState.CONTACTING, serviceManager);
		
		localWorker.workerIsDown();
		workerDAO.workerIsDown(localWorker);

		serviceManager.getDAO(AllocationDAO.class).removeLocalAllocableWorker(failedWorkerID.getPublicKey());
	}

	/**
	 * Notifies that a Worker has recovered
	 * @param recoveredWorkerStub
	 * @param recoveredWorkerPublicKey
	 */
	@Req("REQ019")
	@RecoveryNotification
	public void doNotifyRecovery(WorkerManagement recoveredWorkerStub, DeploymentID recoveredWorkerID) {
		
		assert (recoveredWorkerStub != null) : "recoveredWorkerStub cannot be null";
		
		LocalWorkersDAO workerDAO = serviceManager.getDAO(
				LocalWorkersDAO.class);
		LocalWorker localWorker = WorkerControl.getInstance().getLocalWorker(recoveredWorkerID.getServiceID(), workerDAO);

		if(localWorker == null){
			serviceManager.getLog().error(WorkerMessages.getNotSetWorkerRecoveryMessage(recoveredWorkerID));
			return;
		} 

//TODO		if(localWorker.isUp()){
//			serviceManager.getLog().error(WorkerMessages.getExistentWorkerRecoveryMessage(recoveredWorkerID));
//			return;
//		}
		
		localWorker.workerIsUp(recoveredWorkerStub, recoveredWorkerID);
		workerDAO.workerIsUp(localWorker);
		
		serviceManager.getLog().info(WorkerMessages.getWorkerRecoveryMessage(recoveredWorkerID));
		
		WorkerControl.getInstance().statusChanged(recoveredWorkerID.getServiceID(), 
				localWorker.getStatus(), serviceManager);
		
		//Call setPeer on worker
		WorkerManagementClient workerManagementClient = (WorkerManagementClient) serviceManager.getObjectDeployment(
				PeerConstants.WORKER_MANAGEMENT_CLIENT_OBJECT_NAME).getObject();
		
		AccountingAggregator accounting = (AccountingAggregator) serviceManager.getObjectDeployment(
				PeerConstants.ACCOUNTING_OBJECT_NAME).getObject();
		
		WorkerSpecListener workerSpecListener = (WorkerSpecListener) serviceManager.getObjectDeployment(
				PeerConstants.WORKER_SPEC_LISTENER_OBJECT_NAME).getObject();
		
		recoveredWorkerStub.setPeer(workerManagementClient, accounting, workerSpecListener);
	}

	
}
